#define _GNU_SOURCE
#include "app_serial.h"
#include <unistd.h>
#include "log.h"
#include <fcntl.h>

/**
 * @brief 给一个设备进行串口设置
 * @param 设备指针
 * @return 0 设置成功，-1 设置失败
 */
int app_serial_init(app_device_t *device)
{
    //设置波特率
    int ret= app_serial_set_baudrate(device, BR_115200);
    if(ret==-1)
    {
        log_error(  "设置波特率失败");
        return -1;
    }
    //设置停止位
    ret= app_serial_set_stopbit(device, STOP_BIT_ONE);
    //设置校验位
    ret= app_serial_set_parity(device, PARITY_NONE);

    //设置原始模式
    ret= app_serial_set_raw(device);

    //阻塞模式默认为非阻塞模式

    tcflush(device->fd, TCIOFLUSH); //实质生效

    return 0;
}


/**
 * @brief 设置波特率
 * @brief 设备指针
 * @param 波特率
 * @return 0 设置成功，-1 设置失败
 */
int app_serial_set_baudrate(app_device_t *device, SERIAL_BAUD_RATE baudrate)
{
    //1  取出termios的结构体
    struct termios termios;
    int ret=  tcgetattr(device->fd, &termios);
    if (ret==-1)
    {
        perror(" cfsetspeed tcgetattr 取出失败");
        return -1;
    }

    //2  设置波特率

    ret= cfsetspeed(&termios,baudrate);
    if (ret==-1)
    {
        perror("cfsetspeed 设置失败");
        return -1;
    }

    //3  写入结构体
    ret= tcsetattr(device->fd,TCSAFLUSH,&termios);
    if (ret==-1)
    {
        perror("cfsetspeed  tcsetattr 设置失败");
        return -1;
    }

    return ret;
}



/**
 * @brief 设置停止位
 * @brief 设备指针
 *  @param 停止位
 * @return 0 设置成功，-1 设置失败
 */
int app_serial_set_stopbit(app_device_t *device, SERIAL_STOP_BIT stopbit)
{
    //1  取出termios的结构体
    struct termios termios;
    int ret=  tcgetattr(device->fd, &termios);
    if (ret==-1)
    {
        perror(" cfsetspeed tcgetattr 取出失败");
        return -1;
    }

    //2  设置停止位
    termios.c_cflag &= ~CSTOPB; //清空停止位
    termios.c_cflag |= stopbit; //设置停止位

    //3  写入结构体
    ret= tcsetattr(device->fd,TCSAFLUSH,&termios);
    if (ret==-1)
    {
        perror("cfsetspeed  tcsetattr 设置失败");
        return -1;
    }

    return ret;

}


/**
 * @brief 设置校验位
 * @param 设备指针
 * @param 校验位
 * @return 0 设置成功，-1 设置失败
 */
int app_serial_set_parity(app_device_t *device, SERIAL_PARITY parity)
{
       //1  取出termios的结构体
       struct termios termios;
       int ret=  tcgetattr(device->fd, &termios);
       if (ret==-1)
       {
           perror(" cfsetspeed tcgetattr 取出失败");
           return -1;
       }
   
       //2  设置校验位
       termios.c_cflag &= ~(PARENB | PARODD); // 清除校验位 
       termios.c_cflag |= parity ; //设置校验位
   
       //3  写入结构体
       ret= tcsetattr(device->fd,TCSAFLUSH,&termios);
       if (ret==-1)
       {
           perror("cfsetspeed  tcsetattr 设置失败");
           return -1;
       }
   
       return ret;
}


/**
 * @brief 设置原始模式
 * @param 设备指针
 * @return 0 设置成功，-1 设置失败
 */
int app_serial_set_raw(app_device_t *device)
{
           //1  取出termios的结构体
           struct termios termios;
           int ret=  tcgetattr(device->fd, &termios);
           if (ret==-1)
           {
               perror(" cfsetspeed tcgetattr 取出失败");
               return -1;
           }
       
           //2  设置原始s模式
           cfmakeraw( &termios);
           
       
           //3  写入结构体
           ret= tcsetattr(device->fd,TCSAFLUSH,&termios);
           if (ret==-1)
           {
               perror("cfsetspeed  tcsetattr 设置失败");
               return -1;
           }
       
           return ret;
}

/**
 * @brief 设置阻塞 模式
 * @param 设备指针
 * @param 0 非阻塞，1 阻塞
 * @return 0 设置成功，-1 设置失败
 */
int app_serial_set_block(app_device_t *device, int is_block)
{
      //1  取出termios的结构体
      struct termios termios;
      int ret=  tcgetattr(device->fd, &termios);
      if (ret==-1)
      {
          perror(" cfsetspeed tcgetattr 取出失败");
          return -1;
      }
  
      //2  设置阻塞模式
      if (is_block)
      {
        termios.c_cc[VTIME] = 0; //超时时间
        termios.c_cc[VMIN] = 1;   //读入字符的最少个数
      }else{
        termios.c_cc[VTIME] = 2; //超时时间  200ms
        termios.c_cc[VMIN] = 0;   //读入字符的最少个数
      }
    
      //3  写入结构体
      ret= tcsetattr(device->fd,TCSAFLUSH,&termios);
      if (ret==-1)
      {
          perror("cfsetspeed  tcsetattr 设置失败");
          return -1;
      }
  
      return ret;
}